package com.gwt.server.generator.store;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.gwt.server.domain.Beer;
import com.gwt.server.domain.BeerStyle;

/**
 * Provides a number of Beer objects as a demonstration datasource. Many of the
 * operations in this implementation would be much more efficient in a real
 * database, but are implemented is a straightforward fashion because they're
 * not really important for understanding the RequestFactory framework.
 */
public abstract class BeerSource {
	static class Backing extends BeerSource {
		private Long serial = 0L;

		@Override
		public int countBeers() {
			return beers.size();
		}

		@Override
		public Beer findBeer(String id) {
			return beers.get(id);
		}

		@Override
		public List<Beer> getBeerList(int startIndex, int maxCount) {
			int beersCount = countBeers();

			int start = startIndex;
			if (start >= beersCount) {
				return Collections.emptyList();
			}

			int end = Math.min(startIndex + maxCount, beersCount);
			if (start == end) {
				return Collections.emptyList();
			}

			/*
			 * Otherwise, iterate from the start position until we collect
			 * enough beers or hit the end of the list.
			 */

			Iterator<Beer> it = beers.values().iterator();
			List<Beer> toReturn = new ArrayList<Beer>(maxCount);
			while (toReturn.size() < maxCount && it.hasNext()) {
				toReturn.add(it.next());
			}
			return toReturn;
		}

		@Override
		public void persist(BeerStyle style) {
			style.setVersion(style.getVersion() + 1);
			findBeer(style.getId()).getStyle().copyFrom(style);
		}

		@Override
		public void persist(Beer Beer) {
			if (Beer.getId() == null) {
				Beer.setId(Long.toString(++serial));
			}
			Beer.setVersion(Beer.getVersion() + 1);
			Beer existing = beers.get(Beer.getId());
			if (existing != null) {
				existing.copyFrom(Beer);
			} else {
				beers.put(Beer.getId(), Beer);
			}
		}
	}

	static class CopyOnRead extends BeerSource {
		private final BeerSource backingStore;

		public CopyOnRead(BeerSource backingStore) {
			this.backingStore = backingStore;
		}

		@Override
		public int countBeers() {
			return backingStore.countBeers();
		}

		@Override
		public Beer findBeer(String id) {
			Beer toReturn = beers.get(id);
			if (toReturn == null) {
				toReturn = backingStore.findBeer(id);
				if (toReturn != null) {
					toReturn = toReturn.makeCopy();
				}
				beers.put(id, toReturn);
			}
			return toReturn;
		}

		@Override
		public List<Beer> getBeerList(int startIndex, int maxCount) {
			List<Beer> toReturn = new ArrayList<Beer>(maxCount);
			for (Beer Beer : backingStore.getBeerList(startIndex, maxCount)) {
				Beer copy = findBeer(Beer.getId());
				toReturn.add(copy);
			}
			return toReturn;
		}

		@Override
		public void persist(BeerStyle BeerStyle) {
			backingStore.persist(BeerStyle);
		}

		@Override
		public void persist(Beer Beer) {
			backingStore.persist(Beer);
		}
	}

	/**
	 * Create a BeerSource that will act directly on the given list.
	 */
	public static BeerSource of(List<Beer> beers) {
		BeerSource backing = new Backing();
		for (Beer Beer : beers) {
			backing.persist(Beer);
		}
		return backing;
	}

	/**
	 * Create a BeerSource that will read through to the given source and make
	 * copies of any objects that are requested.
	 */
	public static BeerSource of(BeerSource backing) {
		return new CopyOnRead(backing);
	}

	final Map<String, Beer> beers = new LinkedHashMap<String, Beer>();

	public abstract int countBeers();

	public abstract Beer findBeer(String id);

	public abstract List<Beer> getBeerList(int startIndex, int maxCount);

	public abstract void persist(BeerStyle style);

	public abstract void persist(Beer Beer);
}
